package com.ruoyi.blog.service.impl;

import com.qiniu.api.auth.AuthException;
import com.qiniu.api.auth.digest.Mac;
import com.qiniu.api.rs.PutPolicy;
import com.ruoyi.blog.domain.BlogArticle;
import com.ruoyi.blog.domain.BlogContent;
import com.ruoyi.blog.domain.BlogTag;
import com.ruoyi.blog.mapper.BlogArticleMapper;
import com.ruoyi.blog.mapper.BlogContentMapper;
import com.ruoyi.blog.mapper.BlogTagMapper;
import com.ruoyi.blog.service.IBlogArticleService;
import com.ruoyi.blog.service.IBlogTagService;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 博客文章Service业务层处理
 *
 * @author 刘哲贤
 * @date 2022-08-15
 */
@Service
public class BlogArticleServiceImpl implements IBlogArticleService {
    @Autowired
    private BlogArticleMapper blogArticleMapper;
    @Autowired
    private BlogTagMapper blogTagMapper;
    @Autowired
    private BlogContentMapper blogContentMapper;
    @Autowired
    private IBlogTagService blogTagService;
    @Autowired
    private RedisCache redisCache;

    @Value("${qiniu.accessKey}")
    private String accessKey;

    @Value("${qiniu.secretKey}")
    private String secretKey;

    @Value("${qiniu.bucket}")
    private String bucket;

    @Value("${qiniu.prefix}")
    private String prefix;

    /**
     * 查询博客文章
     *
     * @param id 博客文章主键
     * @return 博客文章
     */
    @Override
    public BlogArticle selectBlogArticleById(Integer id) {
        BlogArticle blogArticle = blogArticleMapper.selectBlogArticleById(id);
        BlogContent content = blogContentMapper.selectBlogContentByArticleId(id);
        blogArticle.setMarkerdownMessage(content.getContent());
        if (blogArticle.getTag() != null && StringUtils.isNotEmpty(blogArticle.getTag())) {
            String[] split = blogArticle.getTag().split(",");
            List<Integer> tagArr = Arrays.stream(split).map(Integer::valueOf).collect(Collectors.toList());
            blogArticle.setTagArr(tagArr);
        }
        return blogArticle;
    }

    /**
     * 后台查询博客文章列表
     *
     * @param blogArticle 博客文章
     * @return 博客文章
     */
    @Override
    public List<BlogArticle> selectBlogArticleList(BlogArticle blogArticle) {
        // 根据登录用户id查询文章列表
        LoginUser loginUser = SecurityUtils.getLoginUser();
        blogArticle.setCreatedUserId(loginUser.getUserId());
        return blogArticleMapper.selectBlogArticleList(blogArticle);
    }

    /**
     * 新增博客文章
     *
     * @param blogArticle 博客文章
     * @return 结果
     */
    @Override
    public int insertBlogArticle(BlogArticle blogArticle) {
        blogArticle.setCreateTime(DateUtils.getNowDate());
        blogArticle.setUpdateTime(blogArticle.getCreateTime());
        blogArticle.setTag(tagListToString(blogArticle.getTagList(), blogArticle.getTag()));
        blogArticleMapper.insertBlogArticle(blogArticle);
        // 将文本内容保存到 blogContent表
        BlogContent content = new BlogContent();
        content.setArticleId(blogArticle.getId().longValue());
        content.setContent(blogArticle.getMarkerdownMessage());
        // 删除redis中的article列表
        flushArticleListRedisCache();
        return blogContentMapper.insertBlogContent(content);
    }

    /**
     * 修改博客文章
     *
     * @param blogArticle 博客文章
     * @return 结果
     */
    @Override
    @Transactional
    public int updateBlogArticle(BlogArticle blogArticle) {
        blogArticle.setUpdateTime(DateUtils.getNowDate());
        blogArticle.setTag(tagListToString(blogArticle.getTagList(), blogArticle.getTag()));
        blogArticleMapper.updateBlogArticle(blogArticle);
        BlogContent content = blogContentMapper.selectBlogContentByArticleId(blogArticle.getId());
        content.setContent(blogArticle.getMarkerdownMessage());
        // 文章编辑后，如果redis存在数据就删除当前文章的缓存数据
        if (redisCache.getCacheObject("articleInfo?id=" + blogArticle.getId()) != null) {
            redisCache.deleteObject("articleInfo?id=" + blogArticle.getId());
        }
        // 删除redis中的article列表
        flushArticleListRedisCache();
        return blogContentMapper.updateBlogContent(content);
    }

    /**
     * 新增或编辑后，删除redis中保存的列表数据
     */
    public void flushArticleListRedisCache() {
        if (redisCache.getCacheObject("articleListTotal") != null) {
            long total = redisCache.getCacheObject("articleListTotal");
            int pageNum;
            if ((int) (total % 10) > 0) {
                pageNum = (int) (total / 10) + 1;
            } else {
                pageNum = (int) (total / 10);
            }
            for (int i = 1; i <= pageNum; i++) {
                if (redisCache.getCacheObject("articleList?pageNum=" + i + "&pageSize=10") != null) {
                    redisCache.deleteObject("articleList?pageNum=" + i + "&pageSize=10");
                }
            }
            redisCache.deleteObject("articleListTotal");
        }
    }

    /**
     * 新增taglist并返回以逗号为间隔的tag 的id
     *
     * @return
     */
    private String tagListToString(List<BlogTag> tagList, String oldTag) {
        if (tagList != null && tagList.size() > 0) {
            List<String> ids = blogTagService.saveBlogTagList(tagList);
            String join = ids.stream().collect(Collectors.joining(",")); // 使用流方法
            oldTag = oldTag + "," + join;
        }
        return oldTag;
    }


    /**
     * 批量删除博客文章
     *
     * @param ids 需要删除的博客文章主键
     * @return 结果
     */
    @Override
    @Transactional
    public int deleteBlogArticleByIds(Integer[] ids) {
        // 批量删除文章内容
        blogContentMapper.deleteBlogContentByArticleIds(ids);
        return blogArticleMapper.deleteBlogArticleByIds(ids);
    }

    /**
     * 删除博客文章信息
     *
     * @param id 博客文章主键
     * @return 结果
     */
    @Override
    public int deleteBlogArticleById(Integer id) {
        return blogArticleMapper.deleteBlogArticleById(id);
    }


    /**
     * 博客端获取文章详情
     *
     * @param id
     * @return
     */
    @Override
    public BlogArticle getBlogArticleById(Integer id) {
        BlogArticle blogArticle;
        // 如果redis没有查到当前id的文章就从数据库查，同时存入redis
        if (redisCache.getCacheObject("articleInfo?id=" + id) == null) {
            blogArticle = blogArticleMapper.selectBlogArticleById(id);
            blogArticle.setCount(blogArticle.getCount() + 1l);
            blogArticleMapper.updateBlogArticle(blogArticle);
            BlogContent content = blogContentMapper.selectBlogContentByArticleId(id);
            blogArticle.setMarkerdownMessage(content.getContent());
            if (blogArticle.getTag() != null && StringUtils.isNotEmpty(blogArticle.getTag())) {
                String[] split = blogArticle.getTag().split(",");
                List<Integer> idsList = Arrays.stream(split).map(Integer::valueOf).collect(Collectors.toList());
                Integer[] ids = idsList.toArray(new Integer[idsList.size()]);
                List<BlogTag> blogTagList = blogTagMapper.selectBlogTagListByIds(ids);
                if (blogTagList != null && blogTagList.size() > 0) {
                    blogArticle.setTagList(blogTagList);
                }
            }
            redisCache.setCacheObject(("articleInfo?id=" + id), blogArticle, 30, TimeUnit.MINUTES);
        } else {
            blogArticle = redisCache.getCacheObject("articleInfo?id=" + id);
            blogArticle.setCount(blogArticle.getCount() + 1l);
            redisCache.setCacheObject("articleInfo?id=" + id, blogArticle);
            // 阅读次数要求精度并不是特别高，所以5秒更新一次
            if (redisCache.getCacheObject("updateArticleCountById?id=" + id) == null) {
                blogArticleMapper.updateBlogArticleCount(blogArticle);
                redisCache.setCacheObject("updateArticleCountById?id=" + id, blogArticle.getCount(), 5, TimeUnit.SECONDS);
            }
        }
        return blogArticle;
    }

    /**
     * 博客端获取文章列表
     *
     * @param blogArticle
     * @return
     */
    @Override
    public List<BlogArticle> selectBlogArticleListFront(BlogArticle blogArticle) {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        List<BlogArticle> articleList;
        // 如果redis没有查到数据就从数据库查，同时存入redis，过期时间半小时
        if (redisCache.getCacheObject("articleList?pageNum=" + pageNum + "&pageSize=" + pageSize) == null) {
            articleList = blogArticleMapper.selectBlogArticleListFront(blogArticle);
            redisCache.setCacheObject("articleList?pageNum=" + pageNum + "&pageSize=" + pageSize, articleList, 30, TimeUnit.MINUTES);
        } else {
            articleList = redisCache.getCacheObject("articleList?pageNum=" + pageNum + "&pageSize=" + pageSize);
        }
        return articleList;
    }

    /**
     * 查询博客总条数
     *
     * @param blogArticle
     * @return
     */
    @Override
    public Long selectBlogArticleListFrontTotal(BlogArticle blogArticle) {
        Long count;
        if (redisCache.getCacheObject("articleListTotal") == null) {
            count = blogArticleMapper.selectBlogArticleListCount(blogArticle);
            redisCache.setCacheObject("articleListTotal", count, 30, TimeUnit.MINUTES);
        } else {
            count = redisCache.getCacheObject("articleListTotal");
        }
        return count;
    }

    @Override
    public AjaxResult getToken() throws AuthException, JSONException {
        AjaxResult ajax = AjaxResult.success();
        ajax.put("prefix",prefix);
        if (redisCache.getCacheObject("qiNiuUploadToken") == null) {
            Mac mac = new Mac(accessKey, secretKey);
            // 请确保该bucket已经存在
            PutPolicy putPolicy = new PutPolicy(bucket);
            putPolicy.expires = 1800l;
            String token = putPolicy.token(mac);
            // 一小时过期
            redisCache.setCacheObject("qiNiuUploadToken",token,30,TimeUnit.MINUTES);
            ajax.put("token", token);
        } else {
            ajax.put("token", redisCache.getCacheObject("qiNiuUploadToken"));
        }
        return ajax;
    }

}
